package eMdP.gestors;

import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;

import com.jme.image.Texture;
import com.jme.math.FastMath;
import com.jme.math.Vector3f;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.state.CullState;
import com.jme.scene.state.TextureState;
import com.jme.system.DisplaySystem;
import com.jme.util.TextureManager;
import com.jme.util.export.JMEImporter;
import com.jme.util.export.Savable;
import com.jme.util.export.binary.BinaryClassLoader;
import com.jme.util.export.binary.BinaryImporter;
import com.jme.util.export.xml.XMLImporter;
import com.jme.util.resource.ResourceLocatorTool;
import com.jme.util.resource.SimpleResourceLocator;
import com.jmex.model.converters.MaxToJme;
import com.jmex.terrain.TerrainPage;
import com.jmex.terrain.util.AbstractHeightMap;
import com.jmex.terrain.util.ImageBasedHeightMap;
import com.mw3d.terrain.model.TerrainAlphaLayer;
import com.mw3d.terrain.model.TerrainBaseLayer;
import com.mw3d.terrain.model.TerrainModel;
import com.mw3d.terrain.model.TerrainPropertiesBean;
import com.mw3d.terrain.model.binary.module.TerrainAlphaLayerModule;
import com.mw3d.terrain.model.binary.module.TerrainBaseLayerModule;
import com.mw3d.terrain.model.binary.module.TerrainModelModule;
import com.mw3d.terrain.model.binary.module.TerrainPropertiesBeanModule;

public class GestorModels {
	
    private static final Logger logger = Logger
            .getLogger(GestorModels.class.getName());
    private static String fileName="";
    
    private static TerrainModel terrainPersistenceModel;
    
    public static final String HEIGHT_MAP_IMAGE = "-terrain-heightmapimage.png";
    
    private static TerrainPage page;
    
    private static Node rootNode = new Node("rootNodeEscenari");
    
    /**
     * Métode estàtic per carregar un model 3d en format *-jme.xml, retorna el model carregat en un Node
     * @param model El nom del model 3d, la ruta s'afageix automàticament
     * @return node Un node amb el model carregat
     */
    public static Node getJMEXML(String model)
    {
    	URL modelToLoad = GestorModels.class.getClassLoader().getResource(model);
    	//System.out.println("Ai cosica! "+model);
    	
    	try {
    		XMLImporter importer = new XMLImporter();
    		Node result = (Node)importer.load(modelToLoad);
			logger.info("ModelImporter:Model *-jme.xml carregat amb éxit!");
			return result;
		} catch (IOException e) {
			System.out.println("ModelImporter::ERROR al carregar el model: "+model);
			return null;
		}    	
    }
    
    public static void afegirTexturaXML(Node personatge, String fitxerTextura)
    {
    	DisplaySystem display = DisplaySystem.getDisplaySystem();
    	System.out.println(fitxerTextura);
    	TextureState ts = display.getRenderer().createTextureState();
		ts.setEnabled(true);
		ts.setTexture(
		TextureManager.loadTexture(
			GestorModels.class.getClassLoader().getResource(fitxerTextura),
			Texture.MinificationFilter.Trilinear,
			Texture.MagnificationFilter.Bilinear)
		);
		personatge.setRenderState(ts);
    }
    
    /**
     * Métode estàtic per carregar un model 3d en format *.3ds, retorna el model carregat en un Node
     * @param direccio El nom del model 3d, la ruta s'afageix automàticament
     * @return node Un node amb el model carregat
     */
    public static Node get3DS(String direccio){
		//direccio = "eMdP/altresModels/"+direccio;
    	
    	URL modelToLoad = GestorModels.class.getClassLoader().getResource(direccio);
		
    	try {
            MaxToJme C1 = new MaxToJme();
            ByteArrayOutputStream BO = new ByteArrayOutputStream();
            C1.convert(new BufferedInputStream(modelToLoad.openStream()), BO);
            Node resultat = (Node)BinaryImporter.getInstance().load(new ByteArrayInputStream(BO.toByteArray()));
            resultat.getLocalRotation().fromAngles(-FastMath.HALF_PI, FastMath.HALF_PI, 0);
            
            logger.info("ModelImporter:Model 3ds carregat amb éxit!");
            return resultat;
        } catch (IOException e) {
            logger.log(Level.SEVERE, "ModelImporter::Error al carregar un model 3ds!", e);
            return null;
        }
    
    }
    
    
    /**
     * Mètode estàtic per a carregar escenaris en format *-jme.xml.
     * @return Node el node de l'escenari
     */
    public static Node getEscenari(String model, String textura)
    {
    	// Carreguem el resource a on es troben les textures.
    	try
    	{
    		ResourceLocatorTool.addResourceLocator(ResourceLocatorTool.TYPE_TEXTURE,
	                new SimpleResourceLocator(GestorModels.class.getClassLoader().getResource("eMdP/escenaris/models/TEX/")));
    	} catch (Exception e)
    	{
    		System.out.println("Peta el try!!");
    	}
    	Node escenari;
    	// Ara carreguem l'escenari
    	if(model.endsWith("-jme.xml"))
    		escenari = getJMEXML("eMdP/escenaris/models/"+model);
    	else{
    		escenari = get3DS("eMdP/escenaris/models/"+model);
    		//escenari.getLocalRotation().fromAngleAxis(FastMath.PI, Vector3f.UNIT_Z);

    	}
    	return escenari;
    }
    
    /**
     * Métode estàtic per carregar un escenari, retorna el model de l'Escenari carregat en un Spatial.
     * El fitxer de l'escenari ha de ser *.jme, juntament amb els fitxers *.terrain i *-terrain-heightmapimage.png
     * @param direccio El nom del model 3d de l'escenari, la ruta s'afageix automàticament
     * @return spatial Un Spatial amb l'Escenari carregat
     */
    public static Spatial getEscenari(String direccio){
	   fileName = "eMdP/escenaris/models/"+direccio;
		
		// first set up the binary modules used to persist out terrain system.
	   setupBinaryClassLoader();
	
	    // Load the terrain configuration file
	   String terrainFileName = fileName.replaceAll(".jme", ".terrain");
	   
	   URL modelToLoad = GestorModels.class.getClassLoader().getResource(terrainFileName);
	    // then lets try to load this file.
	    JMEImporter im = BinaryImporter.getInstance();
	    
	    try {
	        //File file = new File(terrainFileName);
	        //if (file.exists()) {
	            Savable item = im.load(modelToLoad);
	            if (item != null) {
	                terrainPersistenceModel = (TerrainModel) item;
	            }
	        //}else System.out.println("CarregarJME::Error, l'arxiu "+terrainFileName+" no existeix, o no està en el mateix directori!!");
	    } catch (IOException e) {
	    } catch (Exception e) {
	    }
	
	    //File file = null;
	    AbstractHeightMap heightMap = null;
	    // now after load we will check for any available height map to load
	    String fileNameHeighMap = fileName.replaceAll(".jme", HEIGHT_MAP_IMAGE);
	    // now lets try to get a file.
	    modelToLoad = GestorModels.class.getClassLoader().getResource(fileNameHeighMap);
	    try {
	        //file = new File(fileNameHeighMap);
	        //if (file != null) {
	            heightMap = loadHeightMap(modelToLoad.getPath());
	        //}else System.out.println("CarregarJME::Error, l'arxiu "+fileNameHeighMap+" no existeix, o no està en el mateix directori!!");
	    } catch (Exception e) {
	    }
	
	    if (terrainPersistenceModel != null) {
	        if (terrainPersistenceModel.getProperties() != null) {
	            TerrainPropertiesBean bean = terrainPersistenceModel
	                    .getProperties();
	            page = createHeightMapTerrain(heightMap, bean.getSize(),
	                    bean.getBlockSize(), new Vector3f(bean.getXScale(),
	                            bean.getYScale(), bean.getZScale()), false);
	        }
	    }
	    
	    try {
	        Node scene;
	        //File f = new File(fileName);
	        modelToLoad = GestorModels.class.getClassLoader().getResource(fileName);
	        scene = (Node) BinaryImporter.getInstance().load(modelToLoad);
	        rootNode.attachChild(scene);
	    } catch (IOException e) {
	    	System.out.println("ModelImporter::getEscenari:: Error al carregar l'arxiu: "+fileName);
	        e.printStackTrace();
	    }
	
	    if (page != null) {
	        rootNode.attachChild(page);
	    }
	
	    rootNode.updateGeometricState(0, false);
	    rootNode.updateRenderState();
	
	    loadLayers();
	    
	    return rootNode;
	}
	
	private static AbstractHeightMap loadHeightMap(String filepath) {
	    AbstractHeightMap map = null;
	    try {
	        File file = new File(filepath);
	        BufferedImage bufferedImage = ImageIO.read(file);
	        map = new ImageBasedHeightMap(bufferedImage);
	    } catch (IOException e) {
	        e.printStackTrace();
	    }
	    return map;
	}
	
	/**
	 * Load the terrain model modules.
	 */
	private static void setupBinaryClassLoader() {
	    BinaryClassLoader.registerModule(new TerrainModelModule());
	    BinaryClassLoader.registerModule(new TerrainPropertiesBeanModule());
	    BinaryClassLoader.registerModule(new TerrainBaseLayerModule());
	    BinaryClassLoader.registerModule(new TerrainAlphaLayerModule());
	}
	
	private static TerrainPage createHeightMapTerrain(AbstractHeightMap map,
	        int size, int blockSize, Vector3f scale, boolean clod) {
	    TerrainPage terrainPage = new TerrainPage("Terrain", blockSize,
	            size + 1, scale, map.getHeightMap());
	    CullState cs = DisplaySystem.getDisplaySystem().getRenderer().createCullState();
	    cs.setCullFace(CullState.Face.Back );
	    cs.setEnabled(true);
	    terrainPage.setRenderState(cs);
	    return terrainPage;
	}
	
	private static void loadLayers() {
	    TerrainBaseLayer layer = terrainPersistenceModel.getBaseLayer();
	    if (page != null) {
	        SimpleSplatManager manager = new SimpleSplatManager(page, rootNode);
	        if (manager == null)
	            return;
	        if (layer != null) {
	            try {
	                layer.loadFromJme(DisplaySystem.getDisplaySystem().getRenderer());
	                manager.registerBaseLayer(layer);
	            } catch (Exception e) {
	            }
	        }
	
	        ArrayList<TerrainAlphaLayer> alphaLayers = terrainPersistenceModel
	                .getAlphaLayers();
	
	        for (TerrainAlphaLayer alphaLayer : alphaLayers) {
	            alphaLayer.loadFromJme(DisplaySystem.getDisplaySystem().getRenderer());
	            manager.registerAlphaLayer(alphaLayer);
	        }
	    }
	}
	
	
}